package network_programming.np2_nonblocking_communication;

import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

/**
 * 处理者（处理器）
 * 即处理客户的请求
 * */
public class Handler implements Runnable {
    private SocketChannel socketChannel;

    public Handler(SocketChannel socketChannel) {
        this.socketChannel = socketChannel;
    }

    @Override
    public void run() {
        try (Socket socket = socketChannel.socket();     // 获取与socketChannel关联的Socket对象
             BufferedReader br = getReader(socket);
             PrintWriter pw = getWriter(socket)) {
            System.out.println("接收到客户连接，来自：" + socket.getInetAddress() + ":" + socket.getPort());
            String msg;
            while ((msg = br.readLine()) != null) {
                System.out.println(msg);
                pw.println(echo(msg));
//                if (msg.equals("bye"))      // 可能会引发空指针异常（但此处不会，已提前判空了）
                if ("bye".equals(msg))
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private BufferedReader getReader(Socket socket) throws IOException {
        InputStream socketIn = socket.getInputStream();
        return new BufferedReader(new InputStreamReader(socketIn));
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        OutputStream socketOut = socket.getOutputStream();
        return new PrintWriter(socketOut, true);
        // 第二参数设为true则表示此流在每次写入数据时自动刷新
    }

    public String echo(String msg) {
        return "echo: " + msg;
    }

    /**
     * 通过SocketChannel的read(ByteBuffer buffer)方法来读取一行字符串
     * 与BufferedReader的readLine()方法等价
     * */
    public String readLine(SocketChannel socketChannel) throws IOException {
        // 创建一个存放所读到的数据的缓冲区，假定一行字符串对应的字节序列长度小于1024
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 存放一次读取到的数据，一次只读一个字节
        ByteBuffer tempBuffer = ByteBuffer.allocate(1);

        boolean isLine = false;     // 表示是否读到了一行字符串
        boolean isEnd = false;      // 表示是否到达率输入流的末尾
        String data = null;
        Charset charset = Charset.forName("GBK");
        while (!isLine && ! isEnd) {
            tempBuffer.clear();     // 清空缓冲区

            // 在阻塞模式下，只有等读到了1字节或者读到输入流末尾时才返回
            // 在非阻塞模式下，有课能返回0
            int n = socketChannel.read(tempBuffer);
            if (n == -1) {
                isEnd = true;       // 读到输入流的末尾
                break;
            }
            if (n == 0)
                continue;
            tempBuffer.flip();      // 把极限设置为位置，把位置设置为0
            buffer.put(tempBuffer); // 把tempBuffer中的数据拷贝到buffer中
            CharBuffer charBuffer = charset.decode(buffer);     // 解码
            data = charBuffer.toString();
            if (data.contains("\r\n")) {
                isLine = true;      // 读到了一行字符串
                data = data.substring(0, data.indexOf("\r\n"));
                break;
            }
            buffer.position(buffer.limit());    // 设置位置为极限，为下次读取数据做准备
            buffer.limit(buffer.capacity());    // 把极限设置为容量，为下次读取数据做准备
        }
        return data;
    }
}
